Skip to content

fix: pin multiprocessing pools to the fork start method (Python 3.14) - #1439

Merged
Jammy2211 merged 1 commit into
mainfrom
feature/py314-fork-context
Jul 31, 2026
Merged

fix: pin multiprocessing pools to the fork start method (Python 3.14)#1439
Jammy2211 merged 1 commit into
mainfrom
feature/py314-fork-context

Conversation

@Jammy2211

Copy link
Copy Markdown
Collaborator

Summary

Fixes the Python 3.14 blocker (#1437). Python 3.14 changed the default multiprocessing start method on Linux from fork to forkserver; under forkserver, dynesty pool workers receive corrupted model instances (factor-graph fits fail with TypeError: 'Gaussian' object is not iterable) and other pool creation silently degrades to single-CPU fallbacks. Workspace scripts run at module level without __main__ guards, so fork-inheritance semantics are load-bearing across the whole workflow.

A new autofit.non_linear.parallel.fork_context helper reproduces the pre-3.14 default on every platform — fork on POSIX except macOS; the platform default (spawn) on macOS (its default since Python 3.8; fork-with-threads can abort there) and Windows (no fork). It is applied at every pool/process creation site:

  • dynesty pool (_fork_pool_cls subclass — upstream dynesty.pool.Pool.__enter__ hardcodes the default context)
  • AbstractSearch.make_pool
  • SneakyPool / SneakierPool and the Process/Queue layer (covers emcee/zeus)
  • the parallel EP optimiser
  • nautilus — passed a managed fork-context pool object instead of pool=<int>, so it no longer builds internal default-context pools (also gives their cleanup a deterministic with block)

The dynesty single-CPU fallback log now reports the exception that caused it instead of unconditionally claiming the OS doesn't support multiprocessing.

API Changes

None — internal changes only. fork_context is a new helper in autofit.non_linear.parallel; no existing public symbol, signature, or default changes. Runtime behaviour on Python ≤3.13 is identical (the pinned context equals the old platform default everywhere).
See full details below.

Test Plan

  • New test_autofit/non_linear/test_fork_context.py (numpy-only): fork pinning of the helper and Process class, plus a factor-graph likelihood evaluated in a real fork-pool worker asserted equal to the in-process value (the shape-collapse guard for fix: pin fork start method for pools (Python 3.14 forkserver default) #1437).
  • Full test_autofit suite in the worktree: 1641 passed, 2 skipped.
  • Python 3.14.4 venv (full editable stack), default start method: PYAUTO_TEST_MODE=1 PYAUTO_SMALL_DATASETS=1 python autofit_workspace/scripts/overview/overview_1_the_basics.py — previously failed at the factor-graph fit, now completes end-to-end.
  • Same tutorial on the unmodified library reproduces the exact fix: pin fork start method for pools (Python 3.14 forkserver default) #1437 failure, confirming the single-variable root cause.

Follow-ups after merge (separate PRs, per #1437): re-add 3.14 to PyAutoHands python_matrix.yml, add 3.14 classifiers to the libraries, retarget the PyAutoNerves 3.14 experimental banner.

Full API Changes (for automation & release notes)

Added

  • autofit.non_linear.parallel.fork_context() — returns the multiprocessing context PyAutoFit uses for all pools/processes: fork on POSIX except macOS, platform default elsewhere. Reproduces the pre-Python-3.14 default on every platform.

Changed Behaviour

  • On Python 3.14+ Linux, all PyAutoFit-created pools/processes (dynesty, nautilus, emcee/zeus sneaky pools, EP optimiser, make_pool) use the fork start method instead of the new forkserver default — restoring Python ≤3.13 behaviour. No change on Python ≤3.13, macOS, or Windows.
  • The dynesty no-pool fallback log message now includes the triggering exception.

Generated by the PyAutoLabs agent workflow.

🤖 Generated with Claude Code

Python 3.14 changed the default start method on Linux from "fork" to
"forkserver", which corrupts the model instances dynesty pool workers
receive (factor-graph fits fail with "'Gaussian' object is not iterable")
and silently degrades other pool creation to single-CPU fallbacks.

A new autofit.non_linear.parallel.fork_context helper reproduces the
pre-3.14 default on every platform (fork on POSIX except macOS, platform
default on macOS/Windows) and is applied at every pool/process creation
site: the dynesty pool (via a subclass, upstream hardcodes the default
context), make_pool, SneakyPool/SneakierPool, the Process/Queue layer,
the parallel EP optimiser, and nautilus (passed a pool object instead of
an int so it no longer builds its own default-context pool).

The dynesty single-CPU fallback log now includes the exception that
caused it instead of unconditionally blaming the operating system.

Fixes #1437

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pending-release PR queued for the next release build

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant